package org.cb.core;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;

import org.cb.gui.MainGUI;

/**
 * 
 * @author Unknown
 * 
 * All base methods are to go in here.
 *
 */
public abstract class ColourBot implements ScriptBase {
	public static Random random = new Random();
	
	/**
	 * Sleep the thread for X amount of time.
	 * In MS.
	 * 
	 * @param sleepTime
	 */
	public static void sleep(int sleepTime) throws InterruptedException{
		Thread.sleep(sleepTime);
	}
	
	/**
	 * Sleep with a random time
	 * 
	 * @param from
	 * @param to
	 * @throws InterruptedException
	 */
	public static void sleep(int from, int to) throws InterruptedException{
		int sleepTime = random.nextInt(to) + from;
		Thread.sleep(sleepTime);
	}
	
	/**
	 * Get a image of the screen.
	 * 
	 * @return BufferedImage
	 * @throws InterruptedException
	 */
	public static BufferedImage getImage() throws InterruptedException{
		return convertTo16Bit(Global.getTheCanvas().getImage());
	}
	
	public static BufferedImage getImage(Rectangle r) throws InterruptedException{
		return convertTo16Bit(Global.getTheCanvas().getImage(r));
	}
	
	/**
	 * Get a colour at the selected point.
	 * 
	 * @param p
	 * @return Color
	 * @throws InterruptedException
	 */
	public static Color getColor(Point p) throws InterruptedException{
		return new Color(Global.getTheCanvas().getImage().getRGB(p.x, p.y));
	}
	
	/**
	 * Get a colour at the x,y cords.
	 * 
	 * @param x
	 * @param y
	 * @return Color
	 * @throws InterruptedException
	 */
	public static Color getColor(int x, int y) throws InterruptedException{
		return new Color(Global.getTheCanvas().getImage().getRGB(x, y));
	}
	
	/**
	 * Get a colour from the selected image
	 * at the selected point.
	 * 
	 * @param i
	 * @param p
	 * @return Color
	 * @throws InterruptedException
	 */
	public static Color getColor(BufferedImage i, Point p) throws InterruptedException{
		return new Color(i.getRGB(p.x, p.y)); 
	}
	
	/**
	 * Get a colour from the selected image
	 * at the selected x,y cords.
	 * 
	 * @param i
	 * @param x
	 * @param y
	 * @return Color
	 * @throws InterruptedException
	 */
	public static Color getColor(BufferedImage i, int x, int y) throws InterruptedException{
		return new Color(i.getRGB(x, y));
	}
	
	/**
	 * Check the colour at a specific point.
	 * 
	 * @param c
	 * @param p
	 * @return boolean
	 */
	public static boolean checkColour(Rectangle r, Color c, Point p, int tolerance) throws InterruptedException{
		BufferedImage I = getImage(r);
		
		Color cc = new Color(I.getRGB(p.x, p.y));
		if(similarColors(c,cc,tolerance))
			return true;
		else
			log("Color: " + new Color(I.getRGB(p.x, p.y)));

		return false;
	}
	
	/**
	 * Method to find the pixel colour
	 * 
	 * @param c
	 * @return Point
	 */
	public static Point findColour(Rectangle r, Color c, int tolerance) throws InterruptedException{
		BufferedImage I = getImage(r);
		
		for(int y = 0; y < I.getHeight(); y++){
			for(int x = 0; x < I.getWidth(); x++){
				Color cc = new Color(I.getRGB(x, y));
				if(similarColors(c,cc,tolerance))
					return new Point(x + (int)r.getX() ,y + (int)r.getY());
			}
		}
		
		return null;
	}
	
	/**
	 * Method to get all the colours within a set area.
	 * 
	 * @param r
	 * @return Color
	 * @throws InterruptedException
	 */
	public static Color[] getColours(Rectangle r) throws InterruptedException{
		BufferedImage I = getImage(r);
		
		Color[] c = new Color[I.getWidth() * I.getHeight()];
		int count = 0;
		
		for(int y = 0; y < I.getHeight(); y++){
			for(int x = 0; x < I.getWidth(); x++){
				c[count] = new Color(I.getRGB(x, y));
				count++;
			}
		}
		
		return c;
	}
	
	/**
	 * Check if two colours are similar with
	 * a selectable tolerance.
	 * 
	 * @param a
	 * @param b
	 * @param tolerance
	 * @return boolean
	 * @throws InterruptedException
	 */
	public static boolean similarColors(Color a, Color b, int tolerance) throws InterruptedException{
		if (Math.abs(a.getRed() - b.getRed()) > tolerance) return false;
		if (Math.abs(a.getGreen() - b.getGreen()) > tolerance) return false;
		if (Math.abs(a.getBlue() - b.getBlue()) > tolerance) return false;
		return true;
	}
	
	/**
	 * Find the point at which the selected colour
	 * is located within the screen.
	 * 
	 * @param col
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return Point
	 * @throws InterruptedException
	 */
	public static Point findColor(Color col, int x1, int y1, int x2, int y2) throws InterruptedException{
		BufferedImage img = Global.getTheCanvas().getImage();
		for(int y = y1; y < y2; y++) {
			for(int x = x1; x < x2; x++) {
				if(new Color(img.getRGB(x, y)).equals(col)) return new Point(x, y);
			}
		}
		return null;
	}
	
	
	
    /**
     * Should work with any number of Colors > 2.
     * 
     * This method was created by Joel I think
     * from RSCA. Based on Fsig's method.
     *
     * @param r
     * @param colors
     * @param distance
     * @param maxColors
     * @param tolerance
     * @return Point
     * @throws InterruptedException
     */
    public static Point findColorPatternColours(Rectangle r, Color[] colors, int distance, int maxColors, int tolerance) throws InterruptedException {
        Point[][] points = new Point[colors.length][maxColors];
        BufferedImage I = Global.getTheCanvas().getImage();

        try {
            Point[] pointArray;
            for (int a = 0; a < points.length; a++) {
                pointArray = points[a];
                for (int b = 0; b < pointArray.length; b++) {
                    pointArray[b] = findColourInPattern(r, I, colors[a],
                            b == 0 || pointArray[b - 1] == null ? 0 : pointArray[b - 1].x - r.x + 1,
                            b == 0 || pointArray[b - 1] == null ? 0 : pointArray[b - 1].y - r.y, tolerance);
                }
            }

            for (Point[] firstArray : points) {
                for (Point[] secondArray : points) {
                    if (Arrays.equals(firstArray, secondArray)) continue;
                    for (Point firstPoint : firstArray) {
                        if (firstPoint == null) continue;
                        for (Point secondPoint : secondArray) {
                            if (secondPoint == null) continue;
                            if (firstPoint.distance(secondPoint.x, secondPoint.y) < distance) return firstPoint;
                        }
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }

        return null;
    }
    
	/**
	 * Find a colour in the set area with the option to start from
	 * wherever in the image you like.
	 * 
	 * This is a modification of the method Devil and myself originally
	 * came up with.
	 * 
	 * @param r
	 * @param I
	 * @param c
	 * @param x2
	 * @param y2
	 * @return Point
	 * @throws InterruptedException
	 */
	public static Point findColourInPattern(Rectangle r, BufferedImage I, Color c, int x2, int y2, int tolerance) throws InterruptedException{
		Color cc;
		
		for(int y = y2; y < I.getHeight(); y++){
			for(int x = x2; x < I.getWidth(); x++){
				cc = new Color(I.getRGB(x, y));
				if(similarColors(c,cc,tolerance))
					return new Point(x + (int)r.getX() ,y + (int)r.getY());
			}
		}
		
		return null;
	}
	
	/**
	 * Credits Devil for this ideas.
	 * 
	 * This method works alot of magic.
	 * It will take the image and scale the colours down to 16bit.
	 * 
	 * This method will be useful for reducing the colour differences
	 * that scapegoat uses meaning less tolerance to search for and
	 * easier coding.
	 * 
	 * For this reason keep this method hush hush.
	 * 
	 * @param I
	 * @return BufferedImage
	 */
	public static BufferedImage convertTo16Bit(BufferedImage I){
		BufferedImage bi16 = new BufferedImage(I.getWidth(), I.getHeight(), BufferedImage.TYPE_USHORT_555_RGB);
		Graphics2D g2d = (Graphics2D)bi16.getGraphics();
		g2d.drawImage(I, 0, 0, I.getWidth(), I.getHeight(), null);
		return bi16;
	}
	
	/*************************************************
	 * Image methods have been imported to here now.
	 *************************************************/
	
	/**
	 * Find a image within another image (Template Matching)
	 * 
	 * @param img
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @param tolerance
	 * @param backColor
	 * @return Point
	 * @throws InterruptedException
	 */
	public Point findImage(BufferedImage img, int x, int y, int width, int height, int tolerance, Color backColor) throws InterruptedException{
		BufferedImage buffer = Global.getTheCanvas().getImage();
		for(int currentX = x; currentX <= x + width; currentX++) {
			if (currentX <= 765 - img.getWidth()) {
				for(int currentY = y; currentY <= y + height; currentY++) {
					if(currentY <= 503 - img.getHeight()) {
						if(foundImage(buffer, img, currentX, currentY, tolerance, backColor)) { return new Point(currentX, currentY); }
					} else {
						break;
					}
				}
			} else {
				break;
			}
		}
		return null;
	}
	
	/**
	 * This will check if the image matches at the x,y cords.
	 * 
	 * @param buffer
	 * @param img
	 * @param x
	 * @param y
	 * @param tolerance
	 * @param backColor
	 * @return Boolean
	 * @throws InterruptedException
	 */
	private boolean foundImage(BufferedImage buffer, BufferedImage img, int x, int y, int tolerance, Color backColor) throws InterruptedException{
		Color tmpCol;
		try {
			for(int currentX = 0; currentX < img.getWidth(); currentX++) {
				for(int currentY = 0; currentY < img.getHeight(); currentY++) {
					tmpCol = getColor(img, currentX, currentY);
					if(!tmpCol.equals(backColor)) {
						if(!similarColors(getColor(buffer, currentX + x, currentY + y), tmpCol, tolerance))
							return false;
					}
				}
			}
			return true;
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("Had issue: " + x + "," + y);
		}
		return false;
	}
	
	/**
	 * Logs the selected message to the client's log.
	 * 
	 * @param msg
	 */
	public static void log(String msg){
		MainGUI.log(msg);
	}
	

	/*************************************************************
	 * Mouse functions
	 * ***********************************************************/
	
	/**
	 * Returns the current mouse position.
	 * 
	 * @return Point
	 * @throws InterruptedException
	 */
	public Point mousePosition() throws InterruptedException{
		return Global.getTheMouse().mouseXY;
	}
	
	/**
	 * Move the mouse to your selected point.
	 * 
	 * @param p
	 * @throws InterruptedException
	 */
	public void moveMouse(Point p) throws InterruptedException{
		Global.getTheMouse().mouseMove(p);
	}
}
